﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;

namespace NetworkATMForm
{
    public delegate void WrittenMethod(ArgList args);
    public class ComParser
    {
        ArgList args = new ArgList();
        List<ComFormat> coms = new List<ComFormat>();

        public void AddFormat(string mask, WrittenMethod method) { coms.Add(new ComFormat(mask, method)); }
        public Boolean Match(string toCheck, string toComp)
        {
            string checkRem, compRem, compBit;
            int obi, cbi, biti;
            checkRem = toCheck; compRem = toComp;

            obi = compRem.IndexOf('['); cbi = compRem.IndexOf(']');
            while (compRem != "")
            {
                obi = compRem.IndexOf('['); cbi = compRem.IndexOf(']');
                if (obi > -1 && cbi > -1)
                {
                    compBit = compRem.Substring(0, obi);
                    compRem = compRem.Substring(Math.Min(compRem.Length, cbi + 1));
                }
                else
                {
                    compBit = compRem;
                    compRem = "";
                }
                biti = checkRem.IndexOf(compBit);
                if (biti == -1)
                    return false;
                checkRem = checkRem.Substring(biti + compBit.Length);
            }
            return true;
        }

        public Boolean Execute(string toExecute)
        {
            foreach (ComFormat format in coms)
            {
                if (ExecuteSingle(toExecute, format.gMask(), format.gMethod())) return true;
            }
            return false;
        }
        public Boolean ExecuteSingle(string toExecute, string mask, WrittenMethod called)
        {
            args.Clear();
            string exRem, maskRem, maskBit;
            string prevName, curName;
            int obi, cbi, biti;
            bool first = true;
            exRem = toExecute; maskRem = mask;

            prevName = "";
            curName = "";

            biti = 0;

            obi = maskRem.IndexOf('['); cbi = maskRem.IndexOf(']');
            while (maskRem != "")
            {
                obi = maskRem.IndexOf('['); cbi = maskRem.IndexOf(']');
                if (obi > -1 && cbi > -1)
                {
                    maskBit = maskRem.Substring(0, obi);
                    curName = maskRem.Substring(obi + 1, cbi - (obi + 1));
                    maskRem = maskRem.Substring(Math.Min(maskRem.Length, cbi + 1));
                }
                else
                {
                    maskBit = maskRem;
                    curName = null;
                    maskRem = "";
                }
                biti = exRem.IndexOf(maskBit);
                if (biti == -1) return false;
                if (first && biti != 0) return false;
                if (!first) args.Add(prevName, exRem.Substring(0, biti).Trim());
                exRem = exRem.Substring(biti + maskBit.Length);

                first = false;
                prevName = curName;
            }
            if (prevName != null) args.Add(prevName, exRem);

            called(args);

            return true;
        }
    }
    public class ArgBit
    {
        public string name;
        public string value;
        public ArgBit(string newName, string newValue)
        {
            name = newName;
            value = newValue;
        }
    }
    public class ArgList
    {
        List<ArgBit> content = new List<ArgBit>();
        public void Clear()
        {
            content.Clear();
        }
        public void Add(string name, string value)
        {
            content.Add(new ArgBit(name, value));
        }
        public string Get(string key)
        {
            foreach (ArgBit arg in content)
            {
                if (arg.name == key)
                    return arg.value;
            }
            return null;
        }
        public Int32 Num(string key)
        {
            foreach (ArgBit arg in content)
            {
                if (arg.name == key)
                    return Int32.Parse(arg.value);
            }
            return -1;
        }
    }
    public class ComFormat
    {
        string mask;
        WrittenMethod method;
        public ComFormat(string newMask, WrittenMethod newMethod)
        {
            mask = newMask;
            method = newMethod;
        }
        public string gMask() { return mask; }
        public WrittenMethod gMethod() { return method; }
    }

//KLASA AGENTA WĘZŁA SIECIOWEGO
    public class NetnodeAgent
    {
        string nodeName;

        TcpListener listener;
        Timer lt;
        Byte[] listenBuffer = new Byte[256];
        ComParser interpreter = new ComParser();
        Byte[] response = null;

        TcpClient notifier;
        IPEndPoint sipep;
        Form1 myNode;
        public NetnodeAgent(string name, Int32 listenPort, Int32 sendPort, Form1 node)
        {
            NNA(name, new IPEndPoint(IPAddress.Loopback, listenPort), new IPEndPoint(IPAddress.Loopback, sendPort), node);
        }
        public NetnodeAgent(string name, IPEndPoint toListen, IPEndPoint toSend, Form1 node)
        {
            NNA(name, toListen, toSend, node);
        }
        private void NNA(string name, IPEndPoint toListen, IPEndPoint toSend, Form1 node)
        {
            nodeName = name;
            InitializeCommands();

            listener = new TcpListener(toListen);
            listener.Start();
            lt = new Timer();
            lt.Tick += new EventHandler(DoListen);
            lt.Interval = 50;
            lt.Start();

            sipep = toSend;

            myNode = node;
        }
        private void InitializeCommands()
            {
            interpreter.AddFormat("GET WHOIS", RespondWhois);
            interpreter.AddFormat("GET FORWARD:[inport],[invpi],[invci]", GetForward);
            interpreter.AddFormat("SET FORWARD:[inport],[invpi],[invci],[outport],[outvpi],[outvci]", AddForward);
            interpreter.AddFormat("SET REMOVE:[inport],[invpi],[invci]", RemoveForward);
            }
        private void GetForward(ArgList args)
            {
            MakeResponse("GETRSP FORWARD:" + myNode.GetFromAgent(args.Num("inport"), args.Num("invpi"), args.Num("invci")));
            }
        private void AddForward(ArgList args)
            {
            MakeResponse("SETRSP FORWARD:" + myNode.AddFromAgent(">" + args.Get("inport") + "@" + args.Get("invpi") + "@" + args.Get("invci") + "#"
                + args.Get("outport") + "@" + args.Get("outvpi") + "@" + args.Get("outvci")));
            }
        private void RemoveForward(ArgList args)
            {
            MakeResponse("SETRSP REMOVE:" + myNode.RemoveFromAgent(args.Num("inport"), args.Num("invpi"),  args.Num("invci")) );
            }
        private void RespondWhois(ArgList args) { MakeResponse("GETRSP WHOIS:" + nodeName); }

        //nasłuchiwanie na przychodzące polecenia
        private void DoListen(object sender, EventArgs e)
        {
            if (listener.Pending())
            {
                TcpClient speaker = listener.AcceptTcpClient();
                NetworkStream s = speaker.GetStream();

                int count = s.Read(listenBuffer, 0, listenBuffer.Length);
                Call(Encoding.UTF8.GetString(listenBuffer, 0, count));
                if (response != null) s.Write(response, 0, response.Length);
                //do przywrócenia jak będzie wiele aplikacji
                response = null;
            }
        }
        public void Call(string command)
        {
            interpreter.Execute(command);
        }
        public void AmAlive()
        {
            this.SendNotify("KEEPALIVE:" + nodeName);
        }
        private void SendNotify(string notify)
        {
            notifier = new TcpClient();
            notifier.SendBufferSize = 256;
            notifier.SendTimeout = 1000;
            try
            {
                notifier.Connect(sipep);
                Byte[] sendBytes = Encoding.UTF8.GetBytes("NOTIFY " + notify);
                NetworkStream s = notifier.GetStream();
                s.Write(sendBytes, 0, sendBytes.Length);
            }
            catch (SocketException)
            {
                myNode.Print("‼ Nie udało się skomunikować z zarządcą!");
            }
            notifier.Close();
        }
        private void TMPSendResponse(string notify)
        {
            notifier = new TcpClient();
            notifier.SendBufferSize = 256;
            notifier.Connect(sipep);
            Byte[] sendBytes = Encoding.UTF8.GetBytes(notify);
            NetworkStream s = notifier.GetStream();
            s.Write(sendBytes, 0, sendBytes.Length);
            notifier.Close();
        }
        private void MakeResponse(string rsp)
        {
            response = Encoding.UTF8.GetBytes(rsp);
        }
    }

}
